/**
 * Progetto PRAISED.
 * 13/ott/2010
 */
package it.uniroma3.dia.praised.brenda.wrapper;

import it.uniroma3.dia.praised.brenda.model.Enzyme;
import it.uniroma3.dia.praised.brenda.model.Literature;
import it.uniroma3.dia.praised.brenda.model.Synonym;
import it.uniroma3.dia.praised.exception.FactoryException;
import it.uniroma3.dia.praised.exception.WrapperException;
import it.uniroma3.dia.praised.factory.WebClientFactory;
import it.uniroma3.dia.praised.util.WebPageExtractionResult;
import it.uniroma3.dia.praised.util.Utils;

import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlTableDataCell;
import com.gargoylesoftware.htmlunit.html.HtmlTableRow;

/**
 * WrapperBrenda : Classe responsabile dell'estrazione delle informazioni dalla pagina web.
 * Questa classe � responsabile dell'aggiornamento dell'oggetto Enzima mediante l'uso della
 * tecnica del Side-Effect.
 * 
 * @author Accosta Augusto
 * @author Lopez Elisabetta
 * 
 * @see it.uniroma3.dia.praised.brenda.model.Enzyme
 * @version 1.0
 *
 */
public class WrapperBrenda {

	/**
	 *
	 */
	public WrapperBrenda() {
		
	}

	/**
	 * Metodo che restituisce l'Enzima ottenuto estraendo le informazioni dalla pagina web.
	 * 
	 * @param url - URL della pagina web da analizzare
	 * @return enzima - l'enzima aggiornato con le informazioni estratte da questa pagina
	 */
	public void wrapPageIntoEnzyme(Enzyme enzyme, URL url, WebPageExtractionResult extractionResult) throws WrapperException {
		try {
			
			System.out.println("PROCESSING ... " + url.toString());
			
			HtmlPage page = WebClientFactory.newHtmlPage(url);
			
			String id = getTableData(page, "/html/body/table[2]/tbody/tr/td/table/tbody/tr[2]/td[1]");
			String name = getTableData(page, "/html/body/table[3]/tbody/tr/td/table/tbody/tr[2]/td[1]");
			
			/* Reacupero le informazioni relative ai sinonimi */
			List<Synonym> synonyms = this.retrieveSynonymous(page); 
			
			// Inizializzazione Enzima
			enzyme.setID(id);
			enzyme.setName(name);
			enzyme.setSynonyms(synonyms);
			
			extractionResult.setWebPageExtractionResult(true);

		} catch (FailingHttpStatusCodeException e) {
			throw new WrapperException(e);
		} catch (FactoryException e) {
			throw new WrapperException(e);
		}
		
	}
	
	/**
	 * Metodo che estrae le informazioni dalla Tabella in base all'espressione regolare passata come parametro.
	 * 
	 * @param page
	 * @param xpath
	 * @return il contenuto della cella
	 */
	@SuppressWarnings("unchecked")
	private String getTableData(HtmlPage page, String xpath) {
		List<HtmlTableDataCell> byXPath = (List<HtmlTableDataCell>) page.getByXPath(xpath);
		HtmlTableDataCell cell = byXPath.get(0);
		return cell.getTextContent();
	}
	
	/**
	 * Metodo che dato un URL restituisce una lista di abbreviazioni relative alla tabella dei sinonimi contenuta
	 * nella pagina corrispondente all'Enzima.
	 * 
	 * @param url - l'url della pagina di analizzare
	 * @throws WrapperException
	 * @return la lista di abbreviazioni appena estratte
	 */
	@SuppressWarnings("unchecked")
	private List<Synonym> retrieveSynonymous(HtmlPage page) throws WrapperException {
		
		/* Recupero la pagina presente all'indirizzo url */
		try {
				
			/* Recupero la tabella contenente i sinonimi tramite l'utilizzo di un'espressione xpath */			
			String xpath = "//table[@bgcolor='#DCDCDC' and .//a/@name='SYNONYMS']//tr[position() > 1]";
			List<HtmlTableRow> tableRows = (List<HtmlTableRow>) page.getByXPath(xpath);
			
			List<Synonym> synonyms = new ArrayList<Synonym>(tableRows.size());
			for (HtmlTableRow currentTR : tableRows) {
				/* Recupero tutti i td contenuti nel tr corrente */
				if(!currentTR.getCell(0).getTextContent().equals("No entries in this field")) {
					
					Synonym syn = new Synonym();
					
					syn.setName(Utils.nullIfDefault(currentTR.getCell(0).getTextContent()));
					syn.setOrganism(Utils.nullIfDefault(currentTR.getCell(1).getTextContent()));
					syn.setCommentary(Utils.nullIfDefault(currentTR.getCell(2).getTextContent()));

					/* Recupero l'id dell'articolo/i corrispondente/i su PubMed */
					String stringaBrendaID = currentTR.getCell(3).getTextContent();
					String[] brendaIDs = stringaBrendaID.split(",  ");
					Map<String, Literature> brenda2pubmed = new HashMap<String, Literature>(brendaIDs.length);
					for (String brendaID : brendaIDs) {
						if(brendaID.contains("-"))
							continue;
						/* Il parametro del pubmedId lo tengo (al momento) vuoto perch� non � qui che wrappo la pagina di PubMed.
						 * Perché se la wrappo qui assegno al wrapper Brenda la responsabilit� di usare un wrapper PubMed.
						 * Per cui la responsabilit� di wrappare un'altra pagina va assegnata al Crawler che si incaricherà di recuperare
						 * l'URL di PubMed da dove andare ad estrarre il PubMed ID per associarlo al Brenda ID già presente nella mappa.
						 */
						brenda2pubmed.put(brendaID, null);
					}
					syn.setLiterature(brenda2pubmed);
					
					synonyms.add(syn); // Aggiungo il sinonimo
				}
			}
			
			return synonyms;
			
		} catch (FailingHttpStatusCodeException e) {
			throw new WrapperException(e.getMessage()); 
		} catch (ElementNotFoundException e) {
			throw new WrapperException(e.getMessage());
		}
	}

}
